package stations

import (
	"bufio"
	"encoding/json"
	"encoding/xml"
	"ewdetect/config"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/rs/zerolog/log"
)

type Station struct {
	Name       string
	Lat        float64
	Lon        float64
	SampleRate int // Hz
	DC         int32
	NoiseFloor int32
	Calibrated bool
}

type FDSNStationXML struct {
	XMLName  xml.Name      `xml:"FDSNStationXML"`
	Stations []FDSNStation `xml:"Network>Station"`
}

type FDSNStation struct {
	Code      string   `xml:"code,attr"`
	Latitude  float64  `xml:"Latitude"`
	Longitude float64  `xml:"Longitude"`
	Site      FDSNSite `xml:"Site"`
}

type FDSNSite struct {
	Name string `xml:"Name"`
}

var Stations = make(map[string]*map[string]Station)

var URLs = []string{
	"auspass.edu.au:18000",
	"eida.bgr.de:18000",
	"www.cismid.uni.edu.pe:18000",
	"ephesite.ens.fr:18000",
	"geofon.gfz-potsdam.de:18000",
	"link.geonet.org.nz:18000",
	"seis-pub.ga.gov.au:18000",
	"89.22.182.133:18000",
	"finseis.seismo.helsinki.fi:18000",
	"ayiti.unice.fr:18000",
	"ws.icgc.cat:18000",
	"rtserve.ida.ucsd.edu:18000",
	"data.ifz.ru:18000",
	"rtserver.ipgp.fr:18000",
	"rtserve.iris.washington.edu:18000",
	"jamaseis.iris.edu:18000",
	"185.15.171.86:18000",
	"erde.geophysik.uni-muenchen.de:18000",
	"195.96.231.100:18000",
	"earthquakescanada.nrcan.gc.ca:18000",
	"obsebre.es:18000",
	"nam.ogs.it:18000",
	"rtserve.ou.edu:18000",
	"eida.orfeus-eu.org:18000",
	"hudson.igf.edu.pl:18000",
	"161.35.236.45:18000",
	"helis.redsismicabaru.com:18000",
	"rtserve.resif.fr:18000",
	"147.213.113.73:18000",
	"rsis1.on.br:18000",
	"eeyore.seis.sc.edu:6382",
	"rtserve.ird.nc:18000",
	"vibrato.staneo.fr:18000",
	"snac.gein.noa.gr:18000",
	"rtserve.beg.utexas.edu:18000",
	"119.46.126.38:18000",
	"sislink.geofisica.ufrn.br:18000",
	"www.sismocal.org:18000",
	"rtweb.units.it:18000",
	"seedsrv0.ovmp.martinique.univ-ag.fr:18000",
	"clv-cge.uevora.pt:18000",
	"148.213.24.15:18000",
	"worm.uprm.edu:18000",
	"cwbpub.cr.usgs.gov:18000",
	"seisrequest.iag.usp.br:18000",
}

func GetPipeDelimited(stationMap *map[string]Station, URL string, SampleRate int) {
	resp, err := http.Get(URL)
	if err != nil {
		log.Error().Err(err).Str("url", URL).Msg("Failed to get pipe delimited station data")
		return
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Error().Err(err).Msg("Failed to read response body")
		return
	}

	scanner := bufio.NewScanner(strings.NewReader(string(body)))
	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "#") {
			continue
		}
		fields := strings.Split(line, "|")
		if len(fields) < 7 {
			continue
		}
		lat, _ := strconv.ParseFloat(fields[2], 64)
		lon, _ := strconv.ParseFloat(fields[3], 64)
		(*stationMap)[fields[1]] = Station{
			Name:       fields[5],
			Lat:        lat,
			Lon:        lon,
			SampleRate: SampleRate,
		}
		log.Debug().Str("station", fields[1]).Str("name", fields[5]).Msg("Added station")
	}
}

func GetXMLStations(stationMap *map[string]Station, URL string, SampleRate int) {
	resp, err := http.Get(URL)
	if err != nil {
		log.Error().Err(err).Str("url", URL).Msg("Failed to get XML station data")
		return
	}
	defer resp.Body.Close()

	var fdsn FDSNStationXML
	decoder := xml.NewDecoder(resp.Body)
	err = decoder.Decode(&fdsn)
	if err != nil {
		log.Error().Err(err).Msg("Failed to decode XML response")
		return
	}

	for _, station := range fdsn.Stations {
		(*stationMap)[station.Code] = Station{
			Name:       station.Site.Name,
			Lat:        station.Latitude,
			Lon:        station.Longitude,
			SampleRate: SampleRate,
		}
		log.Debug().Str("station", station.Code).Str("name", station.Site.Name).Msg("Added station")
	}
}

func Init() {
	log.Info().Msg("Gathering station metadata")
	if !config.FastStartup {
		log.Info().Msg("Initializing NZ stations")
		NZ := make(map[string]Station)
		Stations["link.geonet.org.nz:18000"] = &NZ
		GetPipeDelimited(&NZ, "https://beta-service.geonet.org.nz/fdsnws/station/1/query?&format=text&level=station", 100)

		log.Info().Msg("Initializing IRIS stations")
		IRIS := make(map[string]Station)
		Stations["rtserve.iris.washington.edu:18000"] = &IRIS
		GetPipeDelimited(&IRIS, "https://service.iris.edu/fdsnws/station/1/query?net=_REALTIME&level=station&format=text&includecomments=true&nodata=404", 100)

		log.Info().Msg("Initializing RESIF stations")
		RESIF := make(map[string]Station)
		Stations["rtserve.resif.fr:18000"] = &RESIF
		GetXMLStations(&RESIF, "https://ws.resif.fr/fdsnws/station/1/query", 100)

		log.Info().Msg("Initializing USP-IAG stations")
		USPIAG := make(map[string]Station)
		Stations["seisrequest.iag.usp.br:18000"] = &USPIAG
		GetXMLStations(&RESIF, "https://seisrequest.iag.usp.br/fdsnws/station/1/query", 100)

		log.Info().Msg("Initializing BGR stations")
		BGR := make(map[string]Station)
		Stations["eida.bgr.de:18000"] = &BGR
		GetXMLStations(&BGR, "https://eida.bgr.de/fdsnws/station/1/query", 100)

		log.Info().Msg("Initializing GEOFON stations")
		GEOFON := make(map[string]Station)
		Stations["geofon.gfz-potsdam.de:18000"] = &GEOFON
		GetXMLStations(&GEOFON, "http://geofon.gfz-potsdam.de/fdsnws/station/1/query?endafter=2024-01-01T00:00:00Z", 100)

		log.Info().Msg("Initializing ICGC stations")
		ICGC := make(map[string]Station)
		Stations["ws.icgc.cat:18000"] = &ICGC
		GetXMLStations(&ICGC, "https://ws.icgc.cat/fdsnws/station/1/query", 100)

		log.Info().Msg("Initializing LMU stations")
		LMU := make(map[string]Station)
		Stations["erde.geophysik.uni-muenchen.de:18000"] = &LMU
		GetXMLStations(&LMU, "https://erde.geophysik.uni-muenchen.de/fdsnws/station/1/query", 100)

		log.Info().Msg("Initializing ORFEUS stations")
		ORFEUS := make(map[string]Station)
		Stations["eida.orfeus-eu.org:18000"] = &ORFEUS
		GetXMLStations(&ORFEUS, "https://www.orfeus-eu.org/fdsnws/station/1/query", 100)

		log.Info().Msg("Initializing NOA stations")
		NOA := make(map[string]Station)
		Stations["snac.gein.noa.gr:18000"] = &NOA
		GetXMLStations(&NOA, "http://snac.gein.noa.gr:8080/fdsnws/station/1/query", 100)

		log.Info().Msg("Initializing AusPass stations")
		AUSPASS := make(map[string]Station)
		Stations["auspass.edu.au:18000"] = &AUSPASS
		GetXMLStations(&AUSPASS, "http://auspass.edu.au:8080/fdsnws/station/1/query", 100)

		log.Info().Msg("Initializing IPGP stations")
		IPGP := make(map[string]Station)
		Stations["rtserver.ipgp.fr:18000"] = &IPGP
		GetXMLStations(&IPGP, "https://ws.ipgp.fr/fdsnws/station/1/query", 100)
		log.Info().Msg("Station initialization complete")
	} else {
		log.Info().Msg("Loading stations from cache")
		stationsJson, err := os.ReadFile("station-metadata-cache.json")
		if err != nil {
			log.Fatal().Err(err).Msg("Failed to read station cache file")
		}

		stationsData := make(map[string]map[string]Station)
		err = json.Unmarshal(stationsJson, &stationsData)
		if err != nil {
			log.Fatal().Err(err).Msg("Failed to unmarshal station data")
		}

		for k, v := range stationsData {
			stationMap := v
			Stations[k] = &stationMap
		}
		log.Info().Msg("Successfully loaded stations from cache")
	}
}

func SerializeStationData() {
	log.Info().Msg("Serializing station data to cache")
	stationsData := make(map[string]map[string]Station)
	for k, v := range Stations {
		stationsData[k] = *v
	}

	stationsJson, err := json.MarshalIndent(stationsData, "", "  ")
	if err != nil {
		log.Error().Err(err).Msg("Failed to marshal station data")
		return
	}

	err = os.WriteFile("station-metadata-cache.json", stationsJson, 0644)
	if err != nil {
		log.Error().Err(err).Msg("Failed to write station cache file")
		return
	}
	log.Info().Msg("Successfully serialized station data to cache")
}
